`timescale 1ns / 1ps
// ********************************************************************
//	FileName	: Uart_Bus.v
//	Author		：hpy
//	Email		：yuan_hp@qq.com
//	Date		：2020年08月09日
//	Description	：uart模块 默认使用12MHz，波特率9600
// --------------------------------------------------------------------

// ****************************
// 波特率模块
// ****************************
module Baud #
(
    parameter				BPS_PARA = 1250 //当使用12MHz时钟时波特率参数选择1250对应9600的波特率
)
(
    input					clk_in,		//系统时钟
    input					rst_n_in,	//系统复位，低有效
    input					bps_en,		//接收或发送时钟使能
    output	reg				bps_clk		//接收或发送时钟输出
);	
 
reg				[12:0]	cnt;
//计数器计数满足波特率时钟要求
always @ (posedge clk_in or negedge rst_n_in) begin
	if(!rst_n_in) 
		cnt <= 1'b0;
	else if((cnt >= BPS_PARA-1)||(!bps_en)) //当时钟信号不使能（bps_en为低电平）时，计数器清零并停止计数
		cnt <= 1'b0;						//当时钟信号使能时，计数器对系统时钟计数，周期为BPS_PARA个系统时钟周期
	else 
		cnt <= cnt + 1'b1;
end
 
//产生相应波特率的时钟节拍，接收模块将以此节拍进行UART数据接收
always @ (posedge clk_in or negedge rst_n_in)
	begin
		if(!rst_n_in) 
			bps_clk <= 1'b0;
		else if(cnt == (BPS_PARA>>1)) 	//BPS_PARA右移一位等于除2，因计数器终值BPS_PARA为数据更替时间点，所以计数器中值时为数据最稳定时间点
			bps_clk <= 1'b1;	
		else 
			bps_clk <= 1'b0;
	end
 
endmodule
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

// ****************************
// 接收数据模块
// ****************************
module Uart_Rx
(
    input					clk_in,			//系统时钟
    input					rst_n_in,		//系统复位，低有效
    
    output	reg				bps_en,			//接收时钟使能
    input					bps_clk,		//接收时钟输入
    
    input					rs232_rx,		//UART接收输入
    output	reg		[7:0]	rx_data			//接收到的数据
);	
 
reg	rs232_rx0,rs232_rx1,rs232_rx2;	
//多级延时锁存去除亚稳态
always @ (posedge clk_in or negedge rst_n_in) begin
	if(!rst_n_in) begin
		rs232_rx0 <= 1'b0;
		rs232_rx1 <= 1'b0;
		rs232_rx2 <= 1'b0;
	end else begin
		rs232_rx0 <= rs232_rx;
		rs232_rx1 <= rs232_rx0;
		rs232_rx2 <= rs232_rx1;
	end
end
 
//检测UART接收输入信号的下降沿
wire	neg_rs232_rx = rs232_rx2 & rs232_rx1 & (~rs232_rx0) & (~rs232_rx);	
 
reg				[3:0]	num;			
//接收时钟使能信号的控制
always @ (posedge clk_in or negedge rst_n_in) begin
	if(!rst_n_in)
		bps_en <= 1'b0;
	else if(neg_rs232_rx && (!bps_en))	//当空闲状态（bps_en为低电平）时检测到UART接收信号下降沿，进入工作状态（bps_en为高电平），控制时钟模块产生接收时钟
		bps_en <= 1'b1;		
	else if(num==4'd9)		      		//当完成一次UART接收操作后，退出工作状态，恢复空闲状态
		bps_en <= 1'b0;			
end
 
reg				[7:0]	rx_data_r;
//当处于工作状态中时，按照接收时钟的节拍获取数据
always @ (posedge clk_in or negedge rst_n_in) begin
	if(!rst_n_in) begin
		num <= 4'd0;
		rx_data <= 8'd0;
		rx_data_r <= 8'd0;
	end else if(bps_en) begin	
		if(bps_clk) begin			
			num <= num+1'b1;
			if(num<=4'd8)
			rx_data_r[num-1]<=rs232_rx;	//先接受低位再接收高位，8位有效数据
		end else if(num == 4'd9) begin	//完成一次UART接收操作后，将获取的数据输出
			num <= 4'd0;			
			rx_data <= rx_data_r;	
		end
	end
end
 
endmodule
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

// ****************************
// 发送数据模块
// ****************************
module Uart_Tx
(
    input					clk_in,			//系统时钟
    input					rst_n_in,		//系统复位，低有效
    output	reg				bps_en,			//发送时钟使能
    input					bps_clk,		//发送时钟输入
    input					rx_bps_en,		//因需要自收自发，使用接收时钟使能判定：接收到新的数据，需要发送
    input			[7:0]	tx_data,		//需要发出的数据
    output	reg				rs232_tx		//UART发送输出
);
 
reg						rx_bps_en_r;
//延时锁存接收时钟使能信号
always @ (posedge clk_in or negedge rst_n_in) begin
	if(!rst_n_in) rx_bps_en_r <= 1'b0;
	else rx_bps_en_r <= rx_bps_en;
end
 
//检测接收时钟使能信号的下降沿，因为下降沿代表接收数据的完成，以此作为发送信号的激励
wire	neg_rx_bps_en = rx_bps_en_r & (~rx_bps_en);
 
reg				[3:0]	num;
reg				[9:0]	tx_data_r;	
//根据接收数据的完成，驱动发送数据操作
always @ (posedge clk_in or negedge rst_n_in) begin
	if(!rst_n_in) begin
		bps_en <= 1'b0;
		tx_data_r <= 8'd0;
	end else if(neg_rx_bps_en)begin	
		bps_en <= 1'b1;						//当检测到接收时钟使能信号的下降沿，表明接收完成，需要发送数据，使能发送时钟使能信号
		tx_data_r <= {1'b1,tx_data,1'b0};	
	end else if(num==4'd10) begin	
		bps_en <= 1'b0;	//一次UART发送需要10个时钟信号，然后结束
	end
end
 
//当处于工作状态中时，按照发送时钟的节拍发送数据
always @ (posedge clk_in or negedge rst_n_in) begin
	if(!rst_n_in) begin
		num <= 1'b0;
		rs232_tx <= 1'b1;
	end else if(bps_en) begin
		if(bps_clk) begin
			num <= num + 1'b1;
			rs232_tx <= tx_data_r[num];
		end else if(num>=4'd10) 
			num <= 4'd0;	
	end
end
 
endmodule
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

//串口顶层模块
module Uart_Bus #
(
    parameter				BPS_PARA = 1250 //当使用12MHz时钟时波特率参数选择1250对应9600的波特率
)
(
    input					clk_in,			//系统时钟
    input					rst_n_in,		//系统复位，低有效
    input					rs232_rx,		//FPGA中UART接收端，分配给UART模块中的发送端TXD
    output					rs232_tx,		//FPGA中UART发送端，分配给UART模块中的接收端RXD
    output    [7:0]         rx_data,       //读到的数据
    input     [7:0]         tx_data        //发送的数据
);		
 
/////////////////////////////////UART接收功能模块例化////////////////////////////////////
wire					bps_en_rx,bps_clk_rx;
//wire			[7:0]	rx_data;
 
//UART接收波特率时钟控制模块 例化
Baud #
(
    .BPS_PARA				(BPS_PARA		)
)
Baud_rx
(	
    .clk_in					(clk_in			),	//系统时钟
    .rst_n_in				(rst_n_in		),	//系统复位，低有效
    .bps_en					(bps_en_rx		),	//接收时钟使能
    .bps_clk				(bps_clk_rx		)	//接收时钟输出
);
 
//UART接收数据模块 例化
Uart_Rx Uart_Rx_uut
(
    .clk_in					(clk_in			),	//系统时钟
    .rst_n_in				(rst_n_in		),	//系统复位，低有效
    .bps_en					(bps_en_rx		),	//接收时钟使能
    .bps_clk				(bps_clk_rx		),	//接收时钟输入
    .rs232_rx				(rs232_rx		),	//UART接收输入
    .rx_data				(rx_data		)	//接收到的数据
);
 
 
/////////////////////////////////UART发送功能模块例化////////////////////////////////////
wire					bps_en_tx,bps_clk_tx;
 
//UART发送波特率时钟控制模块 例化
Baud #
(
    .BPS_PARA				(BPS_PARA		)
)
Baud_tx
(
    .clk_in					(clk_in			),	//系统时钟
    .rst_n_in				(rst_n_in		),	//系统复位，低有效
    .bps_en					(bps_en_tx		),	//发送时钟使能
    .bps_clk				(bps_clk_tx		)	//发送时钟输出
);
 
//UART发送数据模块 例化
Uart_Tx Uart_Tx_uut
(
    .clk_in					(clk_in			),	//系统时钟
    .rst_n_in				(rst_n_in		),	//系统复位，低有效
    .bps_en					(bps_en_tx		),	//发送时钟使能
    .bps_clk				(bps_clk_tx		),	//发送时钟输入
    .rx_bps_en				(bps_en_rx		),	//因需要自收自发，使用接收时钟使能判定：接收到新的数据，需要发送
    .tx_data				(tx_data		),	//需要发出的数据
    .rs232_tx				(rs232_tx		)	//UART发送输出
);
 
endmodule